﻿using HoleDetection.Scorer;
using HoleDetection.Scorer.Models.Abstract;
using HoleDetection.Utils;
using LeBaiTu.Imaging;
using LeBaiTu.Imaging.Files;
using LeBaiTu.Imaging.GDI;
using Microsoft.ML.OnnxRuntime;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HoleDetection
{
    internal class Program
    {
        private static Dictionary<string, Dictionary<string, Rect>> dic = new Dictionary<string, Dictionary<string, Rect>>();
        private static ConcurrentDictionary<string, RectInfo> holeInfos = new ConcurrentDictionary<string, RectInfo>();

        private static void Main(string[] args)
        {
            var assetsRelativePath = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../"));
            string assetsPath = Path.Combine(assetsRelativePath, "Assets");
            var modelFilePath = Path.Combine(assetsPath, "Models", "best-hole.onnx");
            var imagesFolder = Path.Combine(assetsPath, "images");
            var outputFolder = Path.Combine(assetsPath, "images", "output");
            var scorer = new YoloScorer<YoloMYHoleModel>(modelFilePath);
            var tets = @"C:\Users\lbt\Desktop\xinde\11";

            //foreach (ImageNetData imageData in pathList)
            //{
            //    string path = imageData.ImagePath;
            //    string imageName = imageData.Label;
            //    var image = System.Drawing.Image.FromFile(path);
            //    List<YoloPrediction> predictions = scorer.Predict(image);

            //    var graphics = Graphics.FromImage(image);

            //    foreach (var prediction in predictions) // iterate predictions to draw results
            //    {
            //        double score = Math.Round(prediction.Score, 2);

            //        graphics.DrawRectangles(new Pen(prediction.Label.Color, 3),
            //            new[] { prediction.Rectangle });
            //        Console.WriteLine("图片{0}: 得分：{1}", path, score);
            //        var (x, y) = (prediction.Rectangle.X - 3, prediction.Rectangle.Y - 23);

            //        graphics.DrawString($"{prediction.Label.Name} ({score})",
            //            new Font("Consolas", 8, GraphicsUnit.Pixel), new SolidBrush(prediction.Label.Color),
            //            new System.Drawing.PointF(x, y));
            //    }
            //    image.Save(Path.Combine(outputFolder, imageName));
            //}

            long allT = Environment.TickCount;
            long smallPic = NewMain();
            IEnumerable<ImageNetData> pathList = ImageNetData.ReadFromFile(tets); //获取图片路径
            long holeCount = 0;

            #region 非并行

            //long T1 = Environment.TickCount;
            //foreach (ImageNetData imageData in pathList)
            //{
            //    string path = imageData.ImagePath;
            //    string imageName = imageData.FileName;
            //    string fullName = imageData.Label;
            //    Console.WriteLine($"检测图片:{fullName}");
            //    using var image = System.Drawing.Image.FromFile(path);
            //    List<YoloPrediction> predictions = scorer.Predict(image);
            //    holeCount += predictions.Count;
            //    var graphics = Graphics.FromImage(image);

            //    foreach (var prediction in predictions) // iterate predictions to draw results
            //    {
            //        double score = Math.Round(prediction.Score, 2);

            //        graphics.DrawRectangles(new Pen(prediction.Label.Color, 0.5f),
            //            new[] { prediction.Rectangle });
            //        Console.WriteLine("图片{0}: 得分：{1}", path, score);
            //        var (x, y) = (prediction.Rectangle.X - 3, prediction.Rectangle.Y - 23);

            //        graphics.DrawString($"{prediction.Label.Name} ({score})",
            //            new Font("Consolas", 8, GraphicsUnit.Pixel), new SolidBrush(prediction.Label.Color),
            //            new System.Drawing.PointF(x, y));
            //    }
            //    image.Save(Path.Combine(outputFolder, fullName));
            //    var holeIndex = dic[imageName];
            //    MatchHole(predictions, holeIndex);
            //}
            //long T2 = Environment.TickCount;
            //Console.WriteLine("预测耗时：{0}", T2 - T1);

            #endregion 非并行

            #region 并行

            var imageList = new List<ImageNetData>(pathList);
            long T1 = Environment.TickCount;
            try
            {
                Parallel.For(0, imageList.Count, i =>
                {
                    Console.WriteLine($"检测图片:{imageList[i].Label}");
                    string path = imageList[i].ImagePath;
                    var imageName = imageList[i].FileName;
                    using var image = Image.FromFile(path);
                    List<YoloPrediction> predictions = scorer.Predict(image);
                    holeCount += predictions.Count;
                    var holeIndex = dic[imageName];
                    MatchHole(predictions, holeIndex);
                });
            }
            catch (Exception e)
            {
                Console.WriteLine("fail" + e.Message);
            }
            long T2 = Environment.TickCount;
            Console.WriteLine("预测+匹配耗时：{0}ms", T2 - T1); // 19s

            #endregion 并行

            long allT2 = Environment.TickCount;
            Console.WriteLine("拼图+预测+匹配各个小图位置共耗时：{0}ms", allT2 - allT);// 28s
            ////验证孔
            var testPath = @"D:\ProgramFiles\HoleDetectionByYolo\HoleDetection\Assets\images\output\Test";
            var testImage = @"D:\file\images\盲孔抓取测试图\1";//@"C:\Users\lbt\Desktop\xinde";
            var t = @"C:\Users\lbt\Desktop\xinde\11";
            //Task task = Task.Factory.StartNew(() =>
            //{
            //    VerifyHoInfo(testImage, testPath);
            //});

            ////结论
            Console.WriteLine("\n------------------------------数据整理---------------------------------------");
            Console.WriteLine($"原始数据：\n----小图片：{smallPic} 张 共拼成大图：{pathList.Count()} 张");
            Console.WriteLine($"检测数据：\n----预计孔的个数：{smallPic}，实际检测到个数：{holeCount}, 检测率：{(float)holeCount / smallPic}");
            Console.WriteLine($"----预计得到孔信息的个数：{smallPic}，实际得到孔信息的个数：{holeInfos.Count}, 实际获取孔率：{(float)holeInfos.Count / smallPic}");
            Console.ReadKey();
        }

        private static void VerifyHoInfo(string dir, string testPath)
        {
            foreach (KeyValuePair<string, RectInfo> keyValuePair in holeInfos)
            {
                var filePath = Path.Combine(dir, keyValuePair.Key + ".bmp");
                var prediction = keyValuePair.Value;
                var image = System.Drawing.Image.FromFile(filePath);
                if (File.Exists(Path.Combine(testPath, keyValuePair.Key + ".bmp")))
                {
                    Console.WriteLine($"{keyValuePair.Key}.bmp重复");
                }
                if (RectangleExtensions.IsIndexedPixelFormat(image.PixelFormat))
                {
                    var newPath = Path.Combine(@"D:\ProgramFiles\HoleDetectionByYolo\HoleDetection\Assets\images\output\tempory", keyValuePair.Key + ".bmp");
                    using Bitmap bmp = Image.FromFile(filePath) as Bitmap;
                    using Image colorImage = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);
                    Graphics g = Graphics.FromImage(colorImage);
                    g.DrawImage(bmp, 0, 0, bmp.Width, bmp.Height);
                    colorImage.Save(newPath, ImageFormat.Bmp);
                    image = System.Drawing.Image.FromFile(newPath);
                }
                using var graphics = Graphics.FromImage(image);
                graphics.DrawRectangles(new Pen(Color.Red, 0.05f),
                       new[] { prediction.Rect });
                var (x, y) = (prediction.Rect.X - 3, prediction.Rect.Y - 23);
                graphics.DrawString($"{prediction.Label}",
                           new Font("Consolas", 8, GraphicsUnit.Pixel), new SolidBrush(Color.Yellow),
                           new System.Drawing.PointF(x, y));
                image.Save(Path.Combine(testPath, keyValuePair.Key + ".bmp"));
                image.Dispose();
            }
        }

        private static void MatchHole(List<YoloPrediction> predictions, Dictionary<string, Rect> holeIndex)
        {
            float baseWH = 0;
            int maxScoreIndex = 0;
            var score = 0.0;
            foreach (KeyValuePair<string, Rect> pair in holeIndex)
            {
                var fileName = pair.Key;
                var rect = new RectangleF((float)pair.Value.X, (float)pair.Value.Y, (float)pair.Value.Width, (float)pair.Value.Height);
                //for (int i = 0; i < predictions.Count; i++)
                //{
                //    var rectPre = predictions[i].Rectangle;
                //    if (!holeInfos.ContainsKey(fileName) && rect.Contains(rectPre))
                //    {
                //        if (rectPre.Width >= rectPre.Height)
                //        {
                //            baseWH = rectPre.Width + 0.5f;
                //        }
                //        else
                //        {
                //            baseWH = rectPre.Height + 0.5f;
                //        }
                //        var re = new RectangleF(rectPre.X - rect.X - 1.6f, rectPre.Y - rect.Y - 1.8f, baseWH, baseWH);
                //        //var re = new RectangleF(rectPre.X, rectPre.Y, rectPre.Width, rectPre.Height);
                //        //Console.WriteLine($"归一：{(float)Math.Round((re.X + re.Width / 2) * 1f / 146, 6)}-{(float)Math.Round((re.Y + re.Height / 2) * 1f / 174, 6)}-{(float)Math.Round((re.Width * 1.0f / (2 * 73)), 6)}-{(float)Math.Round((re.Height * 1.0f / (2 * 87)), 6)}");
                //        holeInfos.Add(fileName, new RectInfo()
                //        {
                //            Rect = re,
                //            Label = predictions[i].Label.Name
                //        });
                //        break;
                //    }
                //}
                for (int i = 0; i < predictions.Count; i++)
                {
                    var rectPre = predictions[i].Rectangle;

                    if (rectPre.Width >= rectPre.Height)
                    {
                        baseWH = rectPre.Width + 0.5f;
                    }
                    else
                    {
                        baseWH = rectPre.Height + 0.5f;
                    }
                    if (!holeInfos.ContainsKey(fileName) && rect.Contains(rectPre))
                    {
                        var re = new RectangleF(rectPre.X - rect.X - 1.6f, rectPre.Y - rect.Y - 1.8f, baseWH, baseWH);
                        //a.Add(rectPre);
                        holeInfos.TryAdd(fileName, new RectInfo()
                        {
                            Rect = re,
                            Label = predictions[i].Label.Name
                        });
                        maxScoreIndex = i;
                        score = predictions[i].Score;
                    }
                    else if (holeInfos.ContainsKey(fileName) && rect.Contains(rectPre))
                    {
                        //a.Add(rectPre);
                        var rectHole = holeInfos[fileName];
                        var re = new RectangleF(rectPre.X - rect.X - 1.6f, rectPre.Y - rect.Y - 1.8f, baseWH, baseWH);
                        if ((predictions[i].Score > score && re.X > rectHole.Rect.X && re.Y > rectHole.Rect.Y) || (re.X > rectHole.Rect.X && re.Y > rectHole.Rect.Y && re.Width > rectHole.Rect.Width) || (re.Y > rectHole.Rect.Y))
                        {
                            holeInfos[fileName] = new RectInfo()
                            {
                                Rect = re,
                                Label = predictions[i].Label.Name
                            };
                            maxScoreIndex = i;
                            score = predictions[i].Score;
                        }
                    }
                }
            }
        }

        //private static void MatchHole2(List<YoloPrediction> predictions, Dictionary<string, Rect> holeIndex)
        //{
        //    object LockObject = new object();
        //    foreach (KeyValuePair<string, Rect> pair in holeIndex)
        //    {
        //        var fileName = pair.Key;
        //        var rect = new RectangleF((float)pair.Value.X, (float)pair.Value.Y, (float)pair.Value.Width, (float)pair.Value.Height);
        //        Parallel.For(0, predictions.Count, (i, state) =>
        //        {
        //            var rectPre = predictions[i].Rectangle;
        //            if (rect.Contains(rectPre) && !holeInfos.ContainsKey(fileName))
        //            {
        //                var re = new RectangleF(rectPre.X - rect.X - 1, rectPre.Y - rect.Y - 1, rectPre.Width, rectPre.Height);
        //                lock (LockObject)
        //                {
        //                    if (!holeInfos.ContainsKey(fileName))
        //                    {
        //                        holeInfos.Add(fileName, new RectInfo()
        //                        {
        //                            Label = predictions[i].Label.Name,
        //                            Rect = re
        //                        });
        //                        state.Stop();
        //                    }
        //                }
        //            }
        //        });
        //    }
        //}

        private static long NewMain()
        {
            Console.WriteLine("取图拼图开始----------------------");

            int width = 73;
            int height = 87;
            int gridCount = 15;
            long t11 = Environment.TickCount;
            var source = @"D:\file\images\盲孔抓取测试图\1";
            //var source = @"D:\机器学习\newImages\SYq图像";
            var files = new System.Collections.Concurrent.ConcurrentBag<string>(Directory.GetFiles(source)); //D:\ProgramFiles\SignPro\classificationDemo1\HoleDetection\HoleDetection\Assets\images
            //var files = new System.Collections.Concurrent.ConcurrentBag<string>(Directory.GetFiles(@"D:\ProgramFiles\SignPro\classificationDemo1\HoleDetection\HoleDetection\Assets\images")); //D:\ProgramFiles\SignPro\classificationDemo1\HoleDetection\HoleDetection\Assets\images
            //var files = new System.Collections.Concurrent.ConcurrentBag<string>(Directory.GetFiles(@"C:\Users\lbt\Desktop\xinde"));
            // 将文件加载到内存
            Dictionary<string, LeBaiTu.Imaging.BitmapData> images = new Dictionary<string, LeBaiTu.Imaging.BitmapData>();
            LoadImages(files, images);
            long t12 = Environment.TickCount;
            Console.WriteLine("-----------------------读取原图加载内存耗时：{0}ms----------------", t12 - t11); //大概66s

            long fileCount = files.Count;
            var file = "";
            var largeImageCount = (int)Math.Ceiling((float)files.Count / gridCount / gridCount); // 大图数量
            //var temporaryFolder = @"C:\Users\lbt\Desktop\xinde\22";
            var temporaryFolder = @"D:\机器学习\newImages\11123";

            long t1 = Environment.TickCount;
            for (var largeImageIndex = 0; largeImageIndex < largeImageCount; largeImageIndex++)
            {
                using var largeBmp = new System.Drawing.Bitmap(width * gridCount, height * gridCount, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                using var largeBmpAdp = largeBmp.CreateAdapter();
                var rects = new LeBaiTu.Imaging.Rect(0, 0, largeBmp.Width, largeBmp.Height).Grid(gridCount, gridCount);
                var hole = new Dictionary<string, Rect>();
                long t111 = Environment.TickCount;
                foreach (var rect in rects)
                {
                    if (!files.TryTake(out file))
                    {
                        break;
                    }
                    using var sub = images[file];
                    using var subColor = sub.Convert(PixelFormatInfo.BGR_8U);
                    using var subInLarge = largeBmpAdp.CreateSubRef(rect); // 引用大图中的一个子图
                    subColor.CopyPixelsTo(subInLarge); //转换后的小图拷贝到大图当中的Rect区域
                    hole.Add(Path.GetFileNameWithoutExtension(file), rect);
                }
                dic.Add("large_" + largeImageIndex, hole);
                largeBmp.Save($@"C:\Users\lbt\Desktop\xinde\11\large_{largeImageIndex}.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
                long t112 = Environment.TickCount;
                Console.WriteLine($"large_{largeImageIndex}.bmp拼图完成..耗时：{t112 - t111}");
            }
            long t2 = Environment.TickCount;
            Console.WriteLine("-----------------------拼图共计耗时：{0}ms----------------", t2 - t1); //3-4s
            return fileCount;
        }

        private static void LoadImages(System.Collections.Concurrent.ConcurrentBag<string> files, Dictionary<string, LeBaiTu.Imaging.BitmapData> images)
        {
            foreach (var file in files)
            {
                images.Add(file, FileHelper.LoadBitmapFile(file).CloneData());
            }
        }

        private struct RectInfo
        {
            public RectangleF Rect;
            public string Label;
        }
    }
}